package com.codecorp.cortex_scan;

import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.os.Bundle;
import android.os.Vibrator;
import android.preference.ListPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.TwoStatePreference;
import android.widget.Toast;

import com.codecorp.CortexDecoderLibrary.CameraType;
import com.codecorp.CortexDecoderLibrary.Focus;

import java.util.ArrayList;
import java.util.HashSet;

import static android.widget.Toast.*;
import static com.codecorp.internal.Debug.*;
import com.example.ht380d4G_jar_demo.R;

@SuppressWarnings("deprecation")
public class SettingsActivity extends PreferenceActivity implements
        OnSharedPreferenceChangeListener, Preference.OnPreferenceClickListener {

    private static final String TAG = SettingsActivity.class.getSimpleName();
    private ListPreference mFocus;
    private ListPreference mResolution;
    private TwoStatePreference mContinousMode;
    private TwoStatePreference mDebugMode;
    private ListPreference mDebugLevel;
    private ListPreference mCameraType;
    TwoStatePreference mIllumination;
    private TwoStatePreference mVibrator;
    private MyApplication mApplication;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mApplication = (MyApplication) getApplication();
        // Load the XML preferences file
        addPreferencesFromResource(R.xml.preferences);

        mCameraType = (ListPreference) getPreferenceScreen().findPreference("camera_type");
        mFocus = (ListPreference) getPreferenceScreen().findPreference("focus");
        mResolution = (ListPreference) getPreferenceScreen().findPreference(
                "resolution");
        mResolution.setSummary(mResolution.getEntry());
        getPreferenceScreen().findPreference("symbologies")
                .setOnPreferenceClickListener(this);
        mContinousMode = (TwoStatePreference) getPreferenceScreen()
                .findPreference("continuous_scan_mode");
        mIllumination = (TwoStatePreference) getPreferenceScreen().findPreference("illumination");
        mVibrator = (TwoStatePreference) getPreferenceScreen().findPreference("vibrate_on_scan");

        mDebugMode = (TwoStatePreference) getPreferenceScreen().findPreference("debug_mode");
        getPreferenceScreen().removePreference(mDebugMode);

        mDebugLevel = (ListPreference) getPreferenceScreen().findPreference("debug_level");
        getPreferenceScreen().removePreference(mDebugLevel);
        mDebugLevel.setSummary(mDebugLevel.getEntry());

        updateSettings();
    }

    private void updateSettings() {
        debug(TAG, "updateSettings()");
        mIllumination.setEnabled(mApplication.mCortexDecoderLibrary.hasTorch());
        if (!mApplication.mCortexDecoderLibrary.hasTorch())
            mIllumination.setChecked(false);
        // Check if device supports vibration
        mVibrator.setEnabled(((Vibrator)getSystemService(Context.VIBRATOR_SERVICE)).hasVibrator());
        // Set up focus modes
        setupFocusModes();
        // Set up camera types
        setupCameraTypes();
    }

    private void setupFocusModes() {
        final Focus[] focusModes =
                mApplication.mCortexDecoderLibrary.getSupportedFocusModes();
        ArrayList<CharSequence> entries = new ArrayList<CharSequence>();
        ArrayList<CharSequence> values = new ArrayList<CharSequence>();
        HashSet<Focus> supportedModes = new HashSet<Focus>();
        Focus current = focusStringToMode(mFocus.getValue());
        boolean currentModeSupported = false;
        Focus firstSupported = null;
        for (Focus f : Focus.values()) {
            String s = focusModeToString(f);
            if (isFocusModeSupported(focusModes, f)) {
                entries.add(s);
                values.add(s);
                firstSupported = f;
                if (f == current) currentModeSupported = true;
                supportedModes.add(f);
            } else {
                entries.add(s + " (" + getResources().getString(R.string.unsupported) + ")");
                values.add(s);
            }
        }
        mFocus.setEntries(entries.toArray(new CharSequence[0]));
        mFocus.setEntryValues(values.toArray(new CharSequence[0]));
        if (!currentModeSupported) {
            // Fixed Normal has highest priority
            if (supportedModes.contains(Focus.Focus_Fix_Normal)) {
                mFocus.setValue(focusModeToString(Focus.Focus_Fix_Normal));
            }
            else if (firstSupported != null) {
                // Pick the first mode that is supported
                mFocus.setValue(focusModeToString(firstSupported));
            }
        }
        mFocus.setSummary(mFocus.getEntry());
        mFocus.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
            @Override
            public boolean onPreferenceChange(Preference preference, Object newValue) {
                String s = (String) newValue;
                boolean supported = isFocusModeSupported(focusModes, focusStringToMode(s));
                if (!supported)
                    Toast.makeText(SettingsActivity.this, R.string.focus_mode_not_supported,
                            LENGTH_SHORT).show();
                return supported;
            }
        });
    }

    private void setupCameraTypes() {
        final CameraType[] cameraTypes =
                mApplication.mCortexDecoderLibrary.getSupportedCameraTypes();
        ArrayList<CharSequence> entries = new ArrayList<CharSequence>();
        ArrayList<CharSequence> values = new ArrayList<CharSequence>();
        for (CameraType f : CameraType.values()) {
            String s = cameraTypeToString(f);
            if (isCameraTypeSupported(cameraTypes, f)) {
                entries.add(s);
                values.add(s);
            } else {
                entries.add(s + " (" + getResources().getString(R.string.unsupported) + ")");
                values.add(s);
            }
        }
        mCameraType.setEntries(entries.toArray(new CharSequence[0]));
        mCameraType.setEntryValues(values.toArray(new CharSequence[0]));
        mCameraType.setSummary(mCameraType.getEntry());
        mCameraType.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
            @Override
            public boolean onPreferenceChange(Preference preference, Object newValue) {
                String s = (String) newValue;
                boolean supported = isCameraTypeSupported(cameraTypes, cameraTypeStringToEnum(s));
                if (!supported)
                    Toast.makeText(SettingsActivity.this, R.string.camera_type_not_supported,
                            LENGTH_SHORT).show();
                return supported;
            }
        });
    }

    private boolean isFocusModeSupported(Focus[] focusModes, Focus mode) {
        if (focusModes == null) {
            // For some reason CortexScan sent us a null pointer, so let's do the best we can.
            // Normal should be supported on most devices
            return mode == Focus.Focus_Normal;
        }
        for (Focus f : focusModes) {
            if (mode == f) return true;
        }
        return false;
    }

    private boolean isCameraTypeSupported(CameraType[] list, CameraType cameraType) {
        if (list == null) {
            // For some reason CortexScan sent us a null pointer, so let's do the best we can.
            // Back-facing camera should be supported on most devices
            return cameraType == CameraType.BackFacing;
        }
        for (CameraType f : list) {
            if (cameraType == f) return true;
        }
        return false;
    }

    @Override
    public boolean onPreferenceClick(Preference preference) {
        if (preference.getKey().equals("symbologies")) {
            Intent intent = new Intent();
            intent.setClass(this, SymbologiesActivity.class);
            startActivity(intent);
        }
        return false;
    }

    @Override
    protected void onResume() {
        super.onResume();
        getPreferenceScreen().getSharedPreferences()
                .registerOnSharedPreferenceChangeListener(this);
    }

    @Override
    protected void onPause() {
        super.onPause();
        getPreferenceScreen().getSharedPreferences()
                .unregisterOnSharedPreferenceChangeListener(this);
        // We may have opened the camera, so make sure we close it.
        mApplication.mCortexDecoderLibrary.closeCamera();
    }

    boolean noSideEffects = false;

    public void onSharedPreferenceChanged(SharedPreferences prefs,
            String key) {
        debug(TAG, "onSharedPreferenceChanged(" + key + ")");
        if (noSideEffects)
            return;
        mCameraType.setSummary(mCameraType.getEntry());
        mFocus.setSummary(mFocus.getEntry());
        mResolution.setSummary(mResolution.getEntry());
        mDebugLevel.setSummary(mDebugLevel.getEntry());
        if (key.equals("debug_mode")) {
            boolean enabled = prefs.getBoolean(key, false);
            if (enabled) {
                noSideEffects = true;
                mContinousMode.setChecked(false);
                noSideEffects = false;
            }
        } else if (key.equals("continuous_scan_mode")) {
            if (noSideEffects)
                return;
            boolean enabled = prefs.getBoolean(key, false);
            if (enabled) {
                noSideEffects = true;
                mDebugMode.setChecked(false);
                noSideEffects = false;
            }
        } else if (key.equals("camera_type")) {
            mApplication.pushPreference(prefs, key);
            String tmpKey = "focus." + mCameraType.getValue();
            noSideEffects = true;
            if (prefs.contains(tmpKey))
                mFocus.setValue(prefs.getString(tmpKey, mFocus.getValue()));
            tmpKey = "illumination." + mCameraType.getValue();
            if (prefs.contains(tmpKey))
                mIllumination.setChecked(prefs.getBoolean(tmpKey, false));
            noSideEffects = false;
            updateSettings();
        } else if (key.equals("focus")) {
            String tmpKey = "focus." + mCameraType.getValue();
            setPreference(prefs, tmpKey, mFocus.getValue());
        } else if (key.equals("illumination")) {
            String tmpKey = "illumination." + mCameraType.getValue();
            setPreference(prefs, tmpKey, mIllumination.isChecked());
        }
    }

    private void setPreference(SharedPreferences sharedPreferences,
                               String key, Object value) {
        debug(TAG, "setPreference(" + key + ", " + value + ")");
        SharedPreferences.Editor e = sharedPreferences.edit();
        if (value instanceof String)
            e.putString(key, (String) value);
        else if (value instanceof Boolean)
            e.putBoolean(key, (Boolean) value);
        else
            throw new RuntimeException("Programming error!  Unknown value type.");
        e.apply();
    }

    private String focusModeToString(Focus f) {
        if (f == Focus.Focus_Normal)
            return getString(R.string.focus_normal);
        if (f == Focus.Focus_Fix_Normal)
            return getString(R.string.focus_normal_fixed);
        if (f == Focus.Focus_Far)
            return getString(R.string.focus_far);
        if (f == Focus.Focus_Fix_Far)
            return getString(R.string.focus_far_fixed);
        return "ERROR";
    }

    private Focus focusStringToMode(String s) {
        if (s.equals(getString(R.string.focus_normal)))
                return Focus.Focus_Normal;
        if (s.equals(getString(R.string.focus_normal_fixed)))
            return Focus.Focus_Fix_Normal;
        if (s.equals(getString(R.string.focus_far)))
            return Focus.Focus_Far;
        if (s.equals(getString(R.string.focus_far_fixed)))
            return Focus.Focus_Fix_Far;
        return Focus.Focus_Normal;
    }

    private String cameraTypeToString(CameraType f) {
        if (f == CameraType.BackFacing)
            return getString(R.string.camera_type_back);
        if (f == CameraType.FrontFacing)
            return getString(R.string.camera_type_front);
        return "ERROR";
    }

    private CameraType cameraTypeStringToEnum(String s) {
        if (s.equals(getString(R.string.camera_type_back)))
            return CameraType.BackFacing;
        if (s.equals(getString(R.string.camera_type_front)))
            return CameraType.FrontFacing;
        return CameraType.BackFacing;
    }
}
